﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;
using Redbrick.Silverlight.Common;

namespace Redbrick.Silverlight.Common
{

	public class Observable : INotifyPropertyChanged, INotifyPropertyChanging
	{

		#region Declarations

		private static Lazy<Dictionary<object, string>> _propertyNameCache=new Lazy<Dictionary<object,string>>();

		private readonly Guid _id = Guid.NewGuid();
		private bool _isDebug = false;

		#endregion Declarations

		#region Constructor

		public Observable() 
		{
			this.TrackUpdates = true;
		}

		#endregion Constructor

		#region Methods

		Dictionary<object, string> PropertyNameCache { get { return _propertyNameCache.Value; } }
		private string GetPropertyName<_type>(Expression<Func<_type>> lambda)
		{
			if (lambda == null) throw new ArgumentNullException();
			if (!this.PropertyNameCache.ContainsKey(lambda))
			{
				MemberExpression body = lambda.Body as MemberExpression;
				if (body != null)
				{
					this.PropertyNameCache[lambda] = body.Member.Name;
				}
			}
			return this.PropertyNameCache[lambda];
		}

		protected bool RaisePropertyChanged<_type>(Expression<Func<_type>> lambda)
		{
			return this.RaisePropertyChanged(this.GetPropertyName(lambda));
		}
		protected bool RaisePropertyChanged(string property)
		{
			if (this.TrackUpdates)
			{
				if (this.RaisePropertyChanging(property))
				{
					PropertyChangedEventArgs args = new PropertyChangedEventArgs(property);
					var handler = this.PropertyChanged;
					if (handler != null)
					{
						handler(this, args);
					}
					return this.OnPropertyChanged(args);
				}
			}
			return false;
		}

		protected bool RaisePropertyChanging<_type>(Expression<Func<_type>> lambda)
		{
			return this.RaisePropertyChanging(this.GetPropertyName(lambda));
		}
		protected bool RaisePropertyChanging(string property)
		{
			if (this.TrackUpdates)
			{
				PropertyChangingEventArgs args = new PropertyChangingEventArgs(property);
				var handler = this.PropertyChanging;
				if (handler != null)
				{
					handler(this, args);
				}
				return this.OnPropertyChanging(args);
			}
			return false;
		}

		public static void WriteLineIf(bool flag, string msg,bool dump = false)
		{
			if (flag)
			{
				Debug.WriteLine(msg);
			}
		}

		#endregion Methods

		#region Events

		public event PropertyChangedEventHandler PropertyChanged;
		public event PropertyChangingEventHandler PropertyChanging;

		protected virtual bool OnPropertyChanged(PropertyChangedEventArgs args)
		{
			WriteLineIf(this.IsDebug, this.GetType().Name + ":Property Changed:" + args.PropertyName);
			return true;
		}
		protected virtual bool OnPropertyChanging(PropertyChangingEventArgs args)
		{
			WriteLineIf(this.IsDebug && args.Canceled, this.GetType().Name + ": Property Change Canceled: " + args.PropertyName);
			WriteLineIf(this.IsDebug && !args.Canceled, this.GetType().Name + ":Property Changing: " + args.PropertyName);
			return !args.Canceled;
		}


		#endregion Events

		#region Properties

		public Guid ID { get { return this._id; } }
		public bool IsDebug
		{
			get { return this._isDebug; }
			set { if ( value != this._isDebug ) this.RaisePropertyChanged(  () => IsDebug ); }
		}
		public bool HasChanged { get; private set; }
		public bool TrackUpdates { get; set; }

		#endregion Properties

	}
}
